//===============================================================================
// Copyright (C) 2008 Charles River Media. All rights reserved.
// 
// Catmull-Clark subdivision surface viewer program
//
// The developer of the code is
// Gyorgy Antal <gyantal@gmail.com>
//
// Contributor(s):
// Laszlo Szirmay-Kalos <szirmay@iit.bme.hu>
//
// The software was developed for the chapter 
// "Fast Evaluation of Subdivision Surfaces on DirectX10 Graphics Hardware"
// of the book ShaderX 6: Advanced Rendering Techniques, 
// edited by Wolfgang Engel,
// the appropriate section edited by Tom Forsyth. 
//
// The files are provided without support, instruction, or implied warranty of any
// kind.  Charles River Media makes no guarantee of its fitness for a particular purpose and is
// not liable under any circumstances for any damages or loss whatsoever arising
// from the use or inability to use this file or items derived from it.
//===============================================================================
// File: Obj.cpp
//
// Description:
//===============================================================================

#include "obj.h"

//===================================================================
// TOKEN DEFINITIONS
//===================================================================
enum Tokens {
	VERTEX_TOKEN = 1,
	VERTEX_NORMAL_TOKEN,    
	VERTEX_TEXTURE_TOKEN,
	GROUP_TOKEN,
	FACE_TOKEN,
	SEPARATOR_TOKEN,
	MATERIAL_TOKEN,
	SURFACE_TOKEN,
	MTLLIB_TOKEN
};

//---------------------------------------------------------------
//      SpecialCharTable
//---------------------------------------------------------------
SpecialChar specials[] = {
       { '/',          SEPARATOR_TOKEN },     
};

//---------------------------------------------------------------
//      KeywordTable
//---------------------------------------------------------------
Keyword keywords[] = {
       { "v",         VERTEX_TOKEN },      
       { "vn",        VERTEX_NORMAL_TOKEN	},      
       { "vt",        VERTEX_TEXTURE_TOKEN	},      
       { "g",         GROUP_TOKEN   },
       { "f",         FACE_TOKEN   },
	   { "usemtl",	  MATERIAL_TOKEN },
	   { "s",		  SURFACE_TOKEN },
	   { "mtllib",	  MTLLIB_TOKEN },
};




//---------------------------------------------------------------
// OBJ Scanner
//---------------------------------------------------------------
ObjScanner :: ObjScanner(char * n) 
//---------------------------------------------------------------
			: Scanner(n, 
					   specials, sizeof specials / sizeof(SpecialChar), 
					   keywords, sizeof keywords / sizeof(Keyword)) { }

//---------------------------------------------------------------
// OBJ Parser
//---------------------------------------------------------------
void ObjParser :: ParseFile() {
//---------------------------------------------------------------
	GetToken();
	for(; ;) {
		switch (GetCurrentToken()) {
		case VERTEX_TOKEN:			ParseVertex();			break;
		case VERTEX_NORMAL_TOKEN:	ParseVertexNormal();	break;
		case VERTEX_TEXTURE_TOKEN:	ParseVertexTexture();	break;
		case GROUP_TOKEN:			ParseGroup();			break;
		case FACE_TOKEN:			ParseFace();			break;
		case MATERIAL_TOKEN:		ParseMaterial();		break;
		case SURFACE_TOKEN:			ParseSurface();			break;
		case MTLLIB_TOKEN:			ParseMaterialLib();			break;
		default: 
			return;
		}	
	}
}

//---------------------------------------------------------------
void ObjParser :: ParseVertex() {
//---------------------------------------------------------------
	Match(VERTEX_TOKEN);
	float x = GetReal() * scale;
	float y = GetReal() * scale;
	float z = GetReal() * scale;
	Vector vertex = CVector(x, y, z);
	if (nvertex == 0) {
		minX = maxX = vertex.X();
		minY = maxZ = vertex.Y();
		minZ = maxZ = vertex.Z();
	} else {
		if (vertex.X() < minX) minX = vertex.X();
		if (vertex.X() > maxX) maxX = vertex.X();
		if (vertex.Y() < minY) minY = vertex.Y();
		if (vertex.Y() > maxY) maxY = vertex.Y();
		if (vertex.Z() < minZ) minZ = vertex.Z();
		if (vertex.Z() > maxZ) maxZ = vertex.Z();
	}
	vertices[ nvertex++ ] = vertex;
}

//---------------------------------------------------------------
void ObjParser :: ParseVertexNormal() {
//---------------------------------------------------------------
	Match(VERTEX_NORMAL_TOKEN);
	float x = GetReal();
	float y = GetReal();
	float z = GetReal();
	vertex_normals[ nnormal++ ] = CVector(x, y, z);
}

//---------------------------------------------------------------
void ObjParser :: ParseVertexTexture() {
//---------------------------------------------------------------
	Match(VERTEX_TEXTURE_TOKEN);
	float u = GetReal();
	float v = GetReal();
	vertex_textures[ ntexture++ ] = CVector(u, v, 0);
}

//---------------------------------------------------------------
void ObjParser :: ParseGroup() {
//---------------------------------------------------------------
	Match(GROUP_TOKEN);
	while(GetCurrentToken() == NAME_TOKEN) Match(NAME_TOKEN);
}

//---------------------------------------------------------------
void ObjParser :: ParseFace() {
//---------------------------------------------------------------
	Match(FACE_TOKEN);
	for(; GetCurrentToken() == NUMBER_TOKEN; npoly_vertex++) {
		poly_vertexInd[ npoly_vertex ] = GetNumber();
		poly_textureInd[ npoly_vertex ] = 0;
		poly_normalInd[ npoly_vertex ] = 0;
			
		if (GetCurrentToken() == SEPARATOR_TOKEN) {
			Match(SEPARATOR_TOKEN);
			if (GetCurrentToken() == NUMBER_TOKEN) {
				poly_textureInd[ npoly_vertex ] = GetNumber();
			}
			if (GetCurrentToken() == SEPARATOR_TOKEN) {
				Match(SEPARATOR_TOKEN);
				if (GetCurrentToken() == NUMBER_TOKEN) {
					poly_normalInd[ npoly_vertex ] = GetNumber();
				}
			}
		}
	}
	poly_vertexInd[ npoly_vertex ] = -1;
	poly_textureInd[ npoly_vertex ] = -1;
	poly_normalInd[ npoly_vertex ] = -1;
	npoly_vertex++;
}

//---------------------------------------------------------------
void ObjParser :: ParseMaterial() {
//---------------------------------------------------------------
	Match(MATERIAL_TOKEN);
	GetName();
}

//---------------------------------------------------------------
void ObjParser :: ParseSurface() {
//---------------------------------------------------------------
	Match(SURFACE_TOKEN);
	//GetNumber();
	GetName();
}

//---------------------------------------------------------------
void ObjParser :: ParseMaterialLib() {
//---------------------------------------------------------------
	Match(MTLLIB_TOKEN);
	//GetNumber();
	GetName();	// filename
	GetName();	// extension
}
